package edu.upenn.seas.peerreview.smui
import edu.upenn.seas.peerreview.{Id, Sink}

/* ********************************************************** */
// Battleship State Machine

class BSSM(logic: BattleshipLogic, myId:Id, transport: Sink.T, tournament:String) extends BSHandler(myId, transport) {
  
  def rdebug(msg: String) = Debug.debug("nrpc")("[%s] %s".format(myId, msg))
  
  var cfgs : NewGameConfigs = null
  val DO_NOTHING = 0
  var numMsgsRecieved = 0
  var numStillAlive = 0 // including yourself
  var numDefeatedThisTurn = 0
  var myManagerId : Id = null
  var uiId: Id = null
  var expect = 'ReadyToPlay
  
  def broadcastOpps(p:Payload) = broadcast(null, cfgs.opponents, p)
  def forward(from : Id, p : Payload) = send(uiId, Forward(from, p))
  
  private def sendPlayerResultFunc(recip: Id, presult: (Int, Int, Int, Boolean)) {
    if (recip != myId)
      send(recip, PlayerResult(presult._1, presult._2, presult._3, presult._4))
    else {
      forward(myId, PlayerResult(presult._1, presult._2, presult._3, presult._4))
    }
  }
  private def sendFireResultFunc(recip: Id, fresult: (Id, Point, FireResultType)) {
    //FireResult(Envelope(attacker, attackee, FireAt(place, attackee)), result: FireResultType)
    val msg = FireResult(Envelope(fresult._1, myId, FireAt(fresult._2, myId)), fresult._3)
    if (recip == myId)
      handle(Envelope(myId, myId, msg)) // send an implicit fireresult message to yourself
    else
      send(recip, msg)
  }

  def handle(e:Envelope) {
    
    def syncExpect(test: Symbol) {
      if (expect != test)
        println("%s: SYNCHRONIZATION ERROR: waiting for %s, got %s; %s".format(myId, expect, test, e))
    }

    //handle forwarding to UI
    if (e.from != myId) { // don't forward implicit messages
      e.contents match {
      case IAmYourUI() => {
        uiId = e.from
        send(Id.coordinatorId, RequestId(tournament))
      }
      case NewGameConfigs(_,_,_) | PlayerWithdrawal(_) | FireResult(_,_) => send(uiId, e.contents)
      case MoveMade() | PlayerResult(_,_,_,_) => forward(e.from, e.contents)
      case FireAt(_, target) if (e.from != uiId) => forward(e.from, e.contents)
      case GameOver() => { send(uiId, GameOver()); logic.onGameOver(m => send(myId, m)) }
      case TournamentOver() => { send(uiId, TournamentOver()); logic.onTournamentOver(m => send(myId, m)) }
      case _ => DO_NOTHING
      }
    }

    e.contents match {
      case NewGameConfigs(_, _, _) => // Might be a new game!
      case _=> if (!logic.isGameLoopRunning) return
    }

    //handle messages
    e.contents match {
      case ReturnId(id) => { // TODO: verify sender?
        myManagerId = id
        send(myManagerId, JoinServer())
      } 
      case c: NewGameConfigs => {
        cfgs = c
        numStillAlive = c.opponents.size + 1
        logic.onInit(c, myId, uiId, myManagerId)
        numMsgsRecieved = 0
        numDefeatedThisTurn = 0
        expect = 'ReadyToPlay
      }
      case PlaceShip(bow, stern) =>{
        //TODO: verify this is a legal move
        val allShipsPlaced = logic.onPlaceShip(bow, stern)
        if (allShipsPlaced) {
          broadcastOpps(ReadyToPlay())
          handle(Envelope(myId, myId, ReadyToPlay())) // implicit msg to yourself
        }
      }
      case FireAt(coord, target) if(e.from == uiId) => {//my UI is firing  
        send(target, e.contents)
        broadcastOpps(MoveMade())
        handle(Envelope(myId, myId, MoveMade())) // implicit msg to yourself
      }
      case FireAt(coord, target) if(target == myId) => {//I'm being fired at
        logic.onFireAtMe(e.from, coord)
      }
      case MoveMade() => {
        numMsgsRecieved += 1
        syncExpect('MoveMade)
        if (numMsgsRecieved == numStillAlive) {
          numMsgsRecieved = 0
          expect = 'FireResult
          logic.onHandleAttacks(sendFireResultFunc _, sendPlayerResultFunc _)
        }
      }
      case PlayerResult(hits, sunks, kills, isWinner) => {
        if (!isWinner)
          numDefeatedThisTurn += 1
        }
      case FireResult(Envelope(attacker, attackee, FireAt(coord, target)), result) => {
        numMsgsRecieved += 1
        syncExpect('FireResult)
        logic.onFireResult(attacker, target, coord, result)
          
        //wait for each fireresult, including from yourself (added elsewhere)
        if (numMsgsRecieved == numStillAlive) {
          numStillAlive -= numDefeatedThisTurn
          numDefeatedThisTurn = 0
          numMsgsRecieved = 0
          expect = 'MoveMade
          if (numStillAlive <= 1) 
           logic.onLastAlive(sendPlayerResultFunc _)
          else
            send(uiId, RequestAction())
        }
      }
      case ReadyToPlay() =>{
        syncExpect('ReadyToPlay)
        numMsgsRecieved += 1
        if( numMsgsRecieved == numStillAlive ) {
          numMsgsRecieved = 0
          expect = 'MoveMade
          send(uiId, RequestAction())
        }
      }
      case LetMeLeave() => { // UI wants to quit.
        send(myManagerId, LetMeLeave())
      }
      case _ => DO_NOTHING
    }
  }
}

